package com.cch.platform.dao;


import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

import com.cch.platform.util.StringUtil;

@Repository
public class HibernateDao {
	
	@Autowired
	private HibernateTemplate hibernateTemplate;
	
	public <B> B load(Class<B> entityClass,Serializable id) {
		return (B) getHibernateTemplate().load(entityClass, id);
	}

	public <B> B get(Class<B> entityClass,Serializable id) {
		return  (B)getHibernateTemplate().get(entityClass, id);
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <B> B get(Class<B> entityClass,Map param) {
		DetachedCriteria ct=this.createCriteriaOrder(entityClass, param);
		List<B> list=this.getHibernateTemplate().findByCriteria(ct,0,1);
		if(list!=null&&list.size()>0) return  list.get(0);
		else return null;
	}
	
	public <B> List<B> loadAll(Class<B> entityClass) {
		return getHibernateTemplate().loadAll(entityClass);
	}
	
	public Serializable save(Object entity) {
		return getHibernateTemplate().save(entity);
	}

	public void update(Object entity) {
		getHibernateTemplate().update(entity);
	}
	
	public void saveOrUpdate(Object entity){
		getHibernateTemplate().saveOrUpdate(entity);
	}

	public void delete(Object entity) {
		getHibernateTemplate().delete(entity);
	}

	/**
	 * 对延迟加载的实体PO执行初始化
	 * @param entity
	 */
	public void initialize(Object entity) {
		this.getHibernateTemplate().initialize(entity);
	}

	/**
	 * 执行带参的HQL查询
	 * 
	 * @param sql
	 * @param params
	 * @return 查询结果
	 */
	@SuppressWarnings("rawtypes")
	public List find(String hql, Object... params) {
		return this.getHibernateTemplate().find(hql,params);
	}
	
	@SuppressWarnings("rawtypes")
	public List find(final String hql, Map params) throws Exception {
		Query queryObject = this.getSession().createQuery(hql);
		String[] paramsName =queryObject.getNamedParameters();
		for(String paramName:paramsName){
			Object value=params.get(paramName);
			if(value==null){
				throw new Exception("缺少参数："+paramName);
			}
			if (value instanceof Collection) {
				queryObject.setParameterList(paramName, (Collection) value);
			} else if (value instanceof Object[]) {
				queryObject.setParameterList(paramName, (Object[]) value);
			} else {
				queryObject.setParameter(paramName, value);
			}
		}
		return queryObject.list();
	}
	
	@SuppressWarnings("rawtypes")
	public List findNamedQuery(String queryName, Object... params) {
		return this.getHibernateTemplate().findByNamedQuery(queryName,params);
	}
	
	@SuppressWarnings("rawtypes")
	public List findNamedQuery(final String queryName, Map params) throws Exception {
		Query queryObject = this.getSession().getNamedQuery(queryName);
		String[] paramsName =queryObject.getNamedParameters();
		if(paramsName.length>0&&params==null){
			throw new Exception("缺少参数："+paramsName.toString());
		}
		for(String paramName:paramsName){
			Object value=params.get(paramName);
			if(value==null){
				throw new Exception("缺少参数："+paramName);
			}
			if (value instanceof Collection) {
				queryObject.setParameterList(paramName, (Collection) value);
			} else if (value instanceof Object[]) {
				queryObject.setParameterList(paramName, (Object[]) value);
			} else {
				queryObject.setParameter(paramName, value);
			}
		}
		return queryObject.list();
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <B> List<B> find(Class<B> entityClass,Map params) {
		DetachedCriteria ct=this.createCriteriaOrder(entityClass, params);
		return this.getHibernateTemplate().findByCriteria(ct);
	}
	
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public <B> Map<String, Object> pageQuery(Class<B> entityClass,Map<?, ?> params) {
		Assert.notNull(entityClass);
		int pageSize=StringUtil.parseInt(params.get("_pageSize"), 1);
		int pageNo=StringUtil.parseInt(params.get("_pageNo"), 1);
		if(pageNo<1)pageNo=1;
		PageObject page=new PageObject(pageNo, pageSize);
		
		// Count查询
		DetachedCriteria ct=this.createCriteria(entityClass, params);
		ct.setProjection(Projections.rowCount());
		List tlst=this.getHibernateTemplate().findByCriteria(ct);
		long cntnum=(Long) (tlst.get(0));
		page.setTotal(cntnum);
		if (cntnum < 1){
			return page.getModel();
		}

		DetachedCriteria ct2=this.createCriteriaOrder(entityClass, params);
		List<B> datalst=this.getHibernateTemplate().findByCriteria(ct2,page.starIndex(),pageSize);
		page.setRows(datalst);
		return page.getModel();
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private <B> DetachedCriteria createCriteria(Class<B> entityClass,Map params){
		DetachedCriteria ct=DetachedCriteria.forClass(entityClass);
		Map _params = new HashMap();
		Field[] fields=entityClass.getDeclaredFields();
		for(Field field:fields){
			Object v=params.get(field.getName());
			if (v != null) {
				_params.put(field.getName(), v);
			}
		}
		ct.add(Restrictions.allEq(_params));
		return ct;
	}
	
	@SuppressWarnings({ "rawtypes" })
	private <B> DetachedCriteria createCriteriaOrder(Class<B> entityClass,Map params){
		DetachedCriteria ct=this.createCriteria(entityClass, params);
		if(!StringUtil.isEmpty((String)params.get("_sort"))){
			String sort=(String)params.get("_sort");
			String order=(String)params.get("_order");
			if(StringUtil.isEmpty(order)||order.equals("desc")){
				ct.addOrder(Order.desc(sort));
			}else{
				ct.addOrder(Order.asc(sort));
			}
		}
		return ct;
	}
	
	/**
	 * 分页查询函数，使用hql.
	 *
	 * @param pageNo 页号,从1开始.
	 */
	@SuppressWarnings("rawtypes")
	public PageObject pageQuery(String hql, int pageNo, int pageSize, Object... values) {
		Assert.hasText(hql);
		if(pageNo<1)pageNo=1;
		
		PageObject page=new PageObject(pageNo, pageSize);
		
		// Count查询
		String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));
		List countlist = getHibernateTemplate().find(countQueryString, values);
		int totalCount = ((Long)countlist.get(0)).intValue();
		page.setTotal(totalCount);
		if (totalCount < 1){
			return page;
		}
	
		Query query = createQuery(hql, values);
		List rows = query.setFirstResult(page.starIndex()).setMaxResults(pageSize).list();
		page.setRows(rows);
		
		return page;
	}

	/**
	 * 创建Query对象. 对于需要first,max,fetchsize,cache,cacheRegion等诸多设置的函数,可以在返回Query后自行设置.
	 * 留意可以连续设置,如下：
	 * <pre>
	 * dao.getQuery(hql).setMaxResult(100).setCacheable(true).list();
	 * </pre>
	 * 调用方式如下：
	 * <pre>
	 *        dao.createQuery(hql)
	 *        dao.createQuery(hql,arg0);
	 *        dao.createQuery(hql,arg0,arg1);
	 *        dao.createQuery(hql,new Object[arg0,arg1,arg2])
	 * </pre>
	 *
	 * @param values 可变参数.
	 */
	public Query createQuery(String hql, Object... values) {
		Assert.hasText(hql);
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query;
	}
	/**
	 * 去除hql的select 子句，未考虑union的情况,用于pagedQuery.
	 *
	 * @see #pagedQuery(String,int,int,Object[])
	 */
	private static String removeSelect(String hql) {
		Assert.hasText(hql);
		int beginPos = hql.toLowerCase().indexOf("from");
		Assert.isTrue(beginPos != -1, " hql : " + hql + " must has a keyword 'from'");
		return hql.substring(beginPos);
	}
	
	/**
	 * 去除hql的orderby 子句，用于pagedQuery.
	 *
	 * @see #pagedQuery(String,int,int,Object[])
	 */
	private static String removeOrders(String hql) {
		Assert.hasText(hql);
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}

	public HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}

	public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
		this.hibernateTemplate = hibernateTemplate;
	}
	
    public  Session getSession() {
        return SessionFactoryUtils.getSession(hibernateTemplate.getSessionFactory(),true);
    }
}
